// 状态
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class YZPromise {
  status = PROMISE_STATUS_PENDING;
  value = undefined;
  reason = undefined;
  onFulfilledFns = [];
  onRejectedFns = [];

  constructor(executor) {
    const resolve = (value) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return;
          this.status = PROMISE_STATUS_FULFILLED;
          this.value = value;
          this.onFulfilledFns.forEach((fn) => {
            fn(this.value);
          });
        });
      }
    };

    const reject = (reason) => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return;
          this.status = PROMISE_STATUS_REJECTED;
          this.reason = reason;
          this.onRejectedFns.forEach((fn) => {
            fn(this.reason);
          });
        });
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  execCatchError(value, executor, resolve, reject) {
    try {
      const result = executor(value);
      resolve(result);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    onRejected =
      onRejected ||
      ((err) => {
        throw err;
      });
    onFulfilled =
      onFulfilled ||
      ((value) => {
        return value;
      });

    return new YZPromise((resolve, reject) => {
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
        this.execCatchError(this.value, onFulfilled, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
        this.execCatchError(this.reason, onRejected, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_PENDING) {
        if (onFulfilled) {
          this.onFulfilledFns.push(() => {
            this.execCatchError(this.value, onFulfilled, resolve, reject);
          });
        }
        if (onRejected) {
          this.onRejectedFns.push(() => {
            this.execCatchError(this.reason, onRejected, resolve, reject);
          });
        }
      }
    });
  }

  catch(onRejected) {
    this.then(undefined, onRejected);
  }

  finally(onFinally) {
    this.then(
      () => {
        onFinally();
      },
      () => {
        onFinally();
      }
    );
  }

  static resolve(value) {
    return new YZPromise((resolve, reject) => {
      resolve(value);
    });
  }

  static reject(reason) {
    return new YZPromise((resolve, reject) => {
      reject(reason);
    });
  }

  static all(promises) {
    return new YZPromise((resolve, reject) => {
      const result = [],
        length = promises.length;
      for (let i = 0; i < length; i++) {
        promises[i].then(
          (res) => {
            result.push(res);
            if (result.length === length) {
              resolve(result);
            }
          },
          (err) => {
            reject(err);
          }
        );
      }
    });
  }

  static allSettled(promises) {
    return new YZPromise((resolve, reject) => {
      const result = [],
        length = promises.length;
      for (let i = 0; i < length; i++) {
        promises[i].then(
          (res) => {
            result.push({
              status: "fulfilled",
              value: res,
            });
            if (result.length === length) {
              resolve(result);
            }
          },
          (err) => {
            result.push({
              status: "rejected",
              reason: err,
            });
            if (result.length === length) {
              resolve(result);
            }
          }
        );
      }
    });
  }

  static race(promises) {
    return new YZPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (res) => {
            resolve(res);
          },
          (err) => {
            reject(err);
          }
        );
      }
    });
  }

  static any(promises) {
    return new YZPromise((resolve, reject) => {
      const error = [],
        length = promises.length;
      for (let i = 0; i < length; i++) {
        promises[i].then(
          (res) => {
            resolve(res);
          },
          (err) => {
            error.push(err);
            if (error.length === length) {
              reject(".....");
            }
          }
        );
      }
    });
  }
}

const promise = new YZPromise((resolve, reject) => {
  resolve(111);
  // reject(11111);
  // throw new Error("executor message")
});

// then方法多次调用
// console.log("=".repeat(10) + "第一次检查");
// promise
//   .then(
//     (res) => {
//       console.log("res1", res);
//       return "aaa";
//     },
//     (err) => {
//       console.log("err", err);
//       return "bbb";
//     }
//   )
//   .then(
//     (res) => {
//       console.log("res2", res);
//     },
//     (err) => {
//       console.log("err2", err);
//     }
//   );

// resolve, reject
// YZPromise.resolve("aaaa").then((res) => {
//   console.log(res);
// });

// YZPromise.reject("error").catch((err) => {
//   console.log("哎哟", err);
// });

// // all allSettled
const p1 = new YZPromise((resolve, reject) => {
  // setTimeout(() => {resolve(11)}, 1000)
  setTimeout(() => {
    // resolve(11);
    reject(11);
  }, 1000);
});

const p2 = new YZPromise((resolve, reject) => {
  setTimeout(() => {
    // reject(22);
    resolve(22);
  }, 2000);
  // setTimeout(() => {resolve(22)}, 2000)
});

const p3 = new YZPromise((resolve, reject) => {
  // setTimeout(() => {resolve(33)}, 3000)
  setTimeout(() => {
    // resolve(33);
    reject(33);
  }, 3000);
  // setTimeout(() => {resolve(33)}, 500)
});

// YZPromise.all([p1, p2, p3])
//   .then((res) => {
//     console.log(res);
//   })
//   .catch((err) => {
//     console.log(err);
//   });
// YZPromise.allSettled([p1, p2, p3])
//   .then((res) => {
//     console.log(res);
//   })
//   .catch((err) => {
//     console.log(err);
//   });

// // race any
// YZPromise.race([p1, p2, p3])
//   .then((res) => {
//     console.log("res", res);
//   })
//   .catch((err) => {
//     console.log("err", err);
//   });
// err AggregateError: All promises were rejected
YZPromise.any([p1, p2, p3])
  .then((res) => {
    console.log("res", res);
  })
  .catch((err) => {
    console.log("err", err);
  });
